using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using CKS.EBE.Logging;
using CookComputing.XmlRpc;
using Microsoft.SharePoint;
using System.Web;
using System.Diagnostics;

namespace CKS.EBE.API
{
	public class MetaWeblog : XmlRpcService, IMetaWeblogApi
	{
		#region "Structs"
		[StructLayout(LayoutKind.Sequential)]
		public struct BlogDetails
		{
			public string blogid;
			public string url;
			public string blogName;
		}

		[Serializable, StructLayout(LayoutKind.Sequential)]
		public struct CategoryDetails
		{
			public string description;
			public string htmlUrl;
			public string rssUrl;
			public string title;
			public string categoryid;
		}

		[StructLayout(LayoutKind.Sequential), XmlRpcMissingMapping(MappingAction.Ignore)]
		public struct Enclosure
		{
			public int length;
			public string type;
			public string url;
		}

		[StructLayout(LayoutKind.Sequential), XmlRpcMissingMapping(MappingAction.Ignore)]
		public struct MediaObject
		{
			public string name;
			public string type;
			public byte[] bits;
		}

		[Serializable, StructLayout(LayoutKind.Sequential)]
		public struct MediaObjectDetails
		{
			public string url;
		}

		[StructLayout(LayoutKind.Sequential), XmlRpcMissingMapping(MappingAction.Ignore)]
		public struct Post
		{
			[XmlRpcMember(Description = "Required when posting."), XmlRpcMissingMapping(MappingAction.Error)]
			public DateTime dateCreated;
			[XmlRpcMember(Description = "Required when posting."), XmlRpcMissingMapping(MappingAction.Error)]
			public string description;
			[XmlRpcMissingMapping(MappingAction.Error), XmlRpcMember(Description = "Required when posting.")]
			public string title;
			public string[] categories;
			public Enclosure enclosure;
			public string link;
			public string permalink;
			[XmlRpcMember(Description = "Not required when posting. Depending on server may be either string or integer. Use Convert.ToInt32(postid) to treat as integer or Convert.ToString(postid) to treat as string")]
			public object postid;
			public Source source;
			public string userid;
		}

		[StructLayout(LayoutKind.Sequential), XmlRpcMissingMapping(MappingAction.Ignore)]
		public struct Source
		{
			public string name;
			public string url;
		}

		[StructLayout(LayoutKind.Sequential)]
		public struct UserDetails
		{
			public string userid;
			public string firstname;
			public string lastname;
			public string nickname;
			public string email;
			public string url;
		}
		#endregion

		#region IMetaWeblogApi Members

		bool IMetaWeblogApi.deletePost(string appKey, string postid, string username, string password, bool publish)
		{
			SPWeb web = SPContext.Current.Web;
			try
			{
				web.AllowUnsafeUpdates = true;
				SPList postsList = Helper.GetList(web, Localization.BlogList.Posts);
				SPListItem postItem;
				try
				{
					postItem = postsList.GetItemByUniqueId(new Guid(postid));
				}
				catch (Exception)
				{
					throw new XmlRpcFaultException(0, "Could not get a post with the Id.");
				}
				postItem.Recycle();
			}
			catch (Exception ex)
			{
				Trace.Write("Error deleting item: " + ex);
				return false;
			}
			finally
			{
				web.AllowUnsafeUpdates = false;
			}

			return true;
		}

		bool IMetaWeblogApi.editPost(string postid, string username, string password, Post post, bool publish)
		{
			SPWeb web = SPContext.Current.Web;
			try
			{
				web.AllowUnsafeUpdates = true;
				SPList postsList = Helper.GetList(web, Localization.BlogList.Posts);
				SPListItem postItem;
				try
				{
					postItem = postsList.GetItemByUniqueId(new Guid(postid));
				}
				catch (Exception)
				{
					throw new XmlRpcFaultException(0, "Could not get a post with the Id.");
				}

				postItem["Body"] = post.description;
				// Fix for encoding issues with Live Writer
				postItem["Title"] = HttpUtility.HtmlDecode(post.title);

				if (post.dateCreated != DateTime.MinValue)
				{
					postItem["PublishedDate"] = post.dateCreated;
				}

				if (post.categories != null && post.categories.Length > 0)
				{
					postItem[Localization.GetFieldName(Localization.FieldNames.Categories, web.Language)] = BuildCategories(web, post.categories);
				}

				if (publish && postsList.EnableModeration)
				{
					if (!postsList.DoesUserHavePermissions(SPBasePermissions.ApproveItems))
					{
						throw new XmlRpcFaultException(11, "User authentication failed (no Approval permissions).");
					}
					postItem["_ModerationStatus"] = (int)SPModerationStatusType.Approved;
				}

				postItem.Update();
			}
			finally
			{
				web.AllowUnsafeUpdates = false;
			}

			return true;
		}

		private static SPFieldLookupValueCollection BuildCategories(SPWeb web, IEnumerable<string> categories)
		{
			SPList oCategoriesList = Helper.GetList(web, Localization.BlogList.Categories);
			var oCats = new SPFieldLookupValueCollection();

			foreach (string category in categories)
			{
				var query = new SPQuery
									  {
										  Query = string.Format("<Where><Eq><FieldRef Name='Title'/><Value Type='Text'>{0}</Value></Eq></Where>", category)
									  };

				SPListItemCollection results = oCategoriesList.GetItems(query);
				if (results.Count > 0)
				{
					oCats.Add(new SPFieldLookupValue(results[0].ID, category));
				}
			}

			return oCats;
		}

		CategoryDetails[] IMetaWeblogApi.getCategories(string blogid, string username, string password)
		{
			SPWeb web = SPContext.Current.Web;

			SPList oCategories = Helper.GetList(web, Localization.BlogList.Categories);
			var oCats = new List<CategoryDetails>();

			foreach (SPListItem oCat in oCategories.Items)
			{
				var oInfo = new CategoryDetails
									 {
										 categoryid = oCat.ID.ToString(),
										 title = oCat.Title,
										 description = "",
										 htmlUrl = "",
										 rssUrl = ""
									 };

				oCats.Add(oInfo);
			}

			return oCats.ToArray();
		}

		Post IMetaWeblogApi.getPost(string postid, string username, string password)
		{
			SPWeb web = SPContext.Current.Web;
			try
			{
				web.AllowUnsafeUpdates = true;
				SPList postsList = Helper.GetList(web, Localization.BlogList.Posts);
				SPListItem postItem;
				try
				{
					postItem = postsList.GetItemByUniqueId(new Guid(postid));
				}
				catch (Exception)
				{
					throw new XmlRpcFaultException(0, "Could not get a post with the Id.");
				}

				var post = new Post
									{
										userid = (string)postItem["Author"],
										description = (string)postItem["Body"],
										title = postItem.Title,
										link = postItem.Url,
										postid = postItem.UniqueId.ToString(),
										dateCreated = (DateTime)postItem["PublishedDate"],
										categories = AddPostItemCategories(postItem)
									};

				return post;
			}
			catch (Exception ex)
			{
				throw new InvalidOperationException(string.Format("Error getting the post with id {0}: {1}", postid, ex.Message), ex);
			}
			finally
			{
				web.AllowUnsafeUpdates = false;
			}
		}

		Post[] IMetaWeblogApi.getRecentPosts(string blogid, string username, string password, int numberOfPosts)
		{
			SPWeb web = SPContext.Current.Web;
			var oPosts = new List<Post>();

			SPList oList = Helper.GetList(web, Localization.BlogList.Posts);
			var query = new SPQuery
								 {
									 RowLimit = (uint)numberOfPosts,
									 Query = "<OrderBy><FieldRef Name='PublishedDate' Ascending='False' /></OrderBy>"
								 };
			SPListItemCollection postItems = oList.GetItems(query);

			for (int i = 0; i < postItems.Count; i++)
			{
				SPListItem oPost = postItems[i];
				var post = new Post
									{
										userid = (string)oPost["Author"],
										description = (string)oPost["Body"],
										title = oPost.Title,
										link = oPost.Url,
										postid = oPost.UniqueId.ToString(),
										dateCreated = (DateTime)oPost["PublishedDate"],
										categories = AddPostItemCategories(oPost)
									};

				oPosts.Add(post);
			}

			return oPosts.ToArray();
		}

		UserDetails IMetaWeblogApi.getUserInfo(string appKey, string username, string password)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		BlogDetails[] IMetaWeblogApi.getUsersBlogs(string appKey, string username, string password)
		{
			SPWeb web = SPContext.Current.Web;
			var blogDetails = new BlogDetails
										 {
											 blogid = web.ID.ToString(),
											 blogName = web.Title,
											 url = web.Url
										 };

			return new[] { blogDetails };
		}

		MediaObjectDetails IMetaWeblogApi.newMediaObject(string blogid, string username, string password, MediaObject mediaObject)
		{
			SPWeb web = SPContext.Current.Web;
			try
			{
				web.AllowUnsafeUpdates = true;
				SPList mediaList = Helper.GetList(web, Localization.BlogList.Media);

				string[] arParts = mediaObject.name.Split('/');
				SPFolder folder = mediaList.RootFolder;

				int c = 0;
				for (; c < arParts.Length - 1; c++)
				{
					SPFolder oSubFolder = null;
					try
					{
						oSubFolder = folder.SubFolders[arParts[c]];
					}
					catch
					{

					}
					if (oSubFolder == null)
					{
						oSubFolder = folder.SubFolders.Add(arParts[c]);
					}

					folder = oSubFolder;
				}

				SPFileCollection files = folder.Files;
				SPFile newFile = files.Add(string.Format(arParts[c], files.Count), mediaObject.bits, true);

				var details = new MediaObjectDetails
								 {
									 url = web.Url + ((web.Url.EndsWith("/")) ? "" : "/") + newFile.Url
								 };

				return details;
			}
			catch (Exception ex)
			{
				Trace.Write(ex);
				throw;
			}
			finally
			{
				web.AllowUnsafeUpdates = false;
			}
		}

		string IMetaWeblogApi.newPost(string blogid, string username, string password, Post post, bool publish)
		{
			using (var userWeb = new UserWeb())
			{
				SPWeb web = userWeb.Web;
				try
				{
					web.AllowUnsafeUpdates = true;
					//SPList oList = Helper.GetListByFolderName(oWeb, Localization.GetListName(Localization.BlogList.Posts, oWeb.Language));
					SPList postsList = Helper.GetList(web, Localization.BlogList.Posts);
					var query = new SPQuery { RowLimit = 0 };
					SPListItem newPostItem = postsList.GetItems(query).Add();

					// Fix for encoding issues with Live Writer
					newPostItem["Title"] = HttpUtility.HtmlDecode(post.title);
					newPostItem["Body"] = post.description;
					newPostItem["PublishedDate"] = (post.dateCreated == DateTime.MinValue) ? DateTime.Now : post.dateCreated;

					if (publish && postsList.EnableModeration)
					{
						if (!newPostItem.Title.StartsWith("Temporary Post Used For Theme Detection"))
						{
							// only publish the post if it is not a temporary post for theme detection by Windows Live Writer
							if (!postsList.DoesUserHavePermissions(SPBasePermissions.ApproveItems))
							{
								throw new XmlRpcFaultException(11, "User authentication failed (no Approval permissions for Posts-list).");
							}
							newPostItem["_ModerationStatus"] = (int)SPModerationStatusType.Approved;
						}
					}

					try
					{
						if (post.categories != null && post.categories.Length > 0)
						{
							newPostItem[Localization.GetFieldName(Localization.FieldNames.Categories, web.Language)] = BuildCategories(web, post.categories);
						}
					}
					catch (Exception ex)
					{
						Trace.WriteLine("Error during processing of Categories: " + ex);
						throw new InvalidOperationException("Error during processing of Categories: " + ex.Message, ex);
					}

					newPostItem.Update();
					return newPostItem.UniqueId.ToString();
				}
				catch (UnauthorizedAccessException ex)
				{
					Trace.Write(string.Format("The user {0} does not have permissions to create a new post in {1}:\r\n{2}", web.CurrentUser.LoginName, web.Url, ex));
					throw new XmlRpcFaultException(11, "User authentication failed");
				}
				catch (Exception ex)
				{
					Logger.Error(ex, "MetaWebLog.newPost");
					throw new XmlRpcFaultException(0, ex.Message);
				}
				finally
				{
					web.AllowUnsafeUpdates = false;
				}
			}
		}

		#endregion

		/// <summary>
		/// read categories from listitem, and add them to the post
		/// </summary>
		/// <param name="postItem"></param>
		/// <returns></returns>
		private static string[] AddPostItemCategories(SPListItem postItem)
		{
			var arCats = new List<string>();
			var oCats = (SPFieldLookupValueCollection)postItem[Localization.GetFieldName(Localization.FieldNames.Categories, postItem.Web.Language)];
			if (oCats == null || oCats.Count <= 0) return null;

			foreach (SPFieldLookupValue lv in oCats)
			{
				arCats.Add(lv.LookupValue);
			}
			return arCats.ToArray();
		}
	}
}